#include "glwidget.h"
#include <QTimer>
#include <QKeyEvent>
#include <GL/glu.h>
#include <math.h>

GLfloat lightAmbient[4] = { 0.1, 0.1, 0.1, 1.0 };
GLfloat lightDiffuse[4] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat lightPosition[4] = { 5.0, 5.0, 5.0, 1.0 };
GLuint fogMode[3] = { GL_EXP, GL_EXP2, GL_LINEAR };
GLfloat fogColor[4] = { 0.5, 0.5, 0.5, 1.0 };

GLWidget::GLWidget(QWidget *parent) :
    QGLWidget(parent)
{
    fullscreen = false;

    box = top = 0;

    xLoop = yLoop = 0;

    scaling = 1.0;
    xRot = yRot = zRot = 0.0f;
    zoom = -10.0;
    //filter = 0;
    light = false;
    fogFilter = 0;

    setWindowTitle( "Qt OpenGL Test" );

    timerInterval = 20;
        m_timer = new QTimer(this);
        connect(m_timer, SIGNAL(timeout()), this, SLOT(timeOutSlot()));
        m_timer->start(timerInterval);

    if ( fullscreen )
      showFullScreen();

}

void GLWidget::initializeGL()
{
    loadGLTextures();
    buildLists();

    glEnable(GL_TEXTURE_2D); // 启用纹理映射
    glShadeModel(GL_SMOOTH); // 启用阴影平滑
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // 黑色背景
    glClearDepth(1.0f); // 设置深度缓存
    glEnable(GL_DEPTH_TEST); // 启用深度测试
    glDepthFunc( GL_LEQUAL );       //所作深度测试的类型。
    //上面这三行必须做的是关于depth buffer（深度缓存）的。
    //将深度缓存设想为屏幕后面的层。深度缓存不断的对物体进入屏幕内部有多深进行跟踪。
    //我们本节的程序其实没有真正使用深度缓存，但几乎所有在屏幕上显示3D场景OpenGL程序都使用深度缓存。
    //它的排序决定那个物体先画。这样您就不会将一个圆形后面的正方形画到圆形上来。深度缓存是OpenGL十分重要的部分。

    glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
    //真正精细的透视修正。这一行告诉OpenGL我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点。

    glLightfv( GL_LIGHT1, GL_AMBIENT, lightAmbient );
    glLightfv( GL_LIGHT1, GL_DIFFUSE, lightDiffuse );
    glLightfv( GL_LIGHT1, GL_POSITION, lightPosition );
    glEnable( GL_LIGHT1 );

    glFogi( GL_FOG_MODE, fogMode[fogFilter] );
    glFogfv( GL_FOG_COLOR, fogColor );
    glFogf( GL_FOG_DENSITY, 0.005 );
    glHint( GL_FOG_HINT, GL_DONT_CARE );
    glFogf( GL_FOG_START, 1.0 );
    glFogf( GL_FOG_END, 5.0 );
    glEnable( GL_FOG );
}

void GLWidget::resizeGL( int width, int height )
{
    height = height?height:1;    //防止height为0。
    glViewport( 0, 0, (GLint)width, (GLint)height );    //重置当前的视口（Viewport）。

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
}

void GLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glBindTexture(GL_TEXTURE_2D, texture[0]);
    glLoadIdentity();
    glTranslatef(0.0f,0.0f,zoom);
    glScalef(scaling, scaling, scaling);
    glRotatef(xRot, 1.0f, 0.0f, 0.0f);
    glRotatef(yRot, 0.0f, 1.0f, 0.0f);
    glRotatef(zRot, 0.0f, 0.0f, 1.0f);
    glCallList(box);
    glCallList(top);
}

void GLWidget::timeOut()
{
    xRot += 0.16f;
    yRot += 0.20f;
    zRot += 0.12f;

    updateGL();
}

void GLWidget::timeOutSlot()
{
    timeOut();
}

void GLWidget::buildLists()
{
    static const GLfloat vertex_list[8][3] = {
        {-1.0, -1.0, 1.0},
        {1.0, -1.0, 1.0},
        {1.0, 1.0, 1.0},
        {-1.0, 1.0, 1.0},
        {-1.0, -1.0, -1.0},
        {1.0, -1.0, -1.0},
        {1.0, 1.0, -1.0},
        {-1.0, 1.0, -1.0},
    };

    static const GLint vertex_index_list[6][4] = {
        {4, 5, 1, 0},
        {0, 1, 2,  3},
        {4, 7, 6, 5},
        {5, 6, 2, 1},
        {4, 0, 3, 7},
        {7, 6, 2, 3}
    };

    static const GLfloat texCoord_list[4][2] = {
        {0.0, 0.0},
        {1.0, 0.0},
        {1.0, 1.0},
        {0.0, 1.0}
    };

    static const GLint texCoord_index_list[6][4] = {
        {2, 3, 0, 1},
        {0, 1, 2, 3},
        {1, 2, 3, 0},
        {1, 2, 3, 0},
        {0, 1, 2, 3},
        {3, 0, 1, 2}
    };

    static const GLfloat normal_list[6][3] = {
        {0.0, -1.0, 0.0},
        {0.0, 0.0, 1.0},
        {0.0, 0.0, -1.0},
        {1.0, 0.0, 0.0},
        {-1.0, 0.0, 0.0},
        {0.0, 1.0, 0.0}
    };

    box = glGenLists(2);
    glNewList(box, GL_COMPILE);
    glBegin(GL_QUADS);
    for (int i = 0; i < 5; i++) {
        glNormal3fv(normal_list[i]);
        for (int j = 0; j < 4; j++) {
            glTexCoord2fv(texCoord_list[texCoord_index_list[i][j]]);
            glVertex3fv(vertex_list[vertex_index_list[i][j]]);
        }
    }
    glEnd();
    glEndList();

    top = box + 1;
    glNewList(top, GL_COMPILE);
    glBegin(GL_QUADS);
    glNormal3fv(normal_list[5]);
    for (int i = 0; i < 4; i++) {
        glTexCoord2fv(texCoord_list[texCoord_index_list[5][i]]);
        glVertex3fv(vertex_list[vertex_index_list[5][i]]);
    }
    glEnd();
    glEndList();
}

void GLWidget::loadGLTextures()
{
    QString strImages[6] = {"crate.bmp", "crate.bmp", "crate.bmp","crate.bmp", "crate.bmp", "crate.bmp"};
    glGenTextures(6, &texture[0]); // 创建纹理
    for (int i = 0; i < 6; ++i) {
        QImage t;
        //! 注意: 图片的宽度和高度必须为2的N次方
        QImage b;
        if(!b.load(":/images/" + strImages[i]))
        {
            QImage dummy(256, 256, QImage::Format_RGB32);
            dummy.fill( Qt::green );
            b = dummy;
        }
        t = QGLWidget::convertToGLFormat(b);

        glBindTexture(GL_TEXTURE_2D, texture[i]);
        glTexImage2D(GL_TEXTURE_2D, 0, 3, t.width(), t.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, t.bits());
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // 线性滤波
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    }
}

void GLWidget::mousePressEvent(QMouseEvent *e)
{
    lastPos = e->pos();
}

void GLWidget::mouseMoveEvent(QMouseEvent *e)
{
    GLfloat dx = GLfloat(e->x() - lastPos.x()) / width();
    GLfloat dy = GLfloat(e->y() - lastPos.y()) / height();
    if (e->buttons() & Qt::LeftButton) {
        xRot -= 180 * dy;
        yRot -= 180 * dx;
        updateGL();
    } else if (e->buttons() & Qt::RightButton) {
        xRot += 180 * dy;
        zRot += 180 * dx;
        updateGL();
    }
    lastPos = e->pos();
}

void GLWidget::wheelEvent(QWheelEvent *e)
{
    double numDegrees = -e->delta() / 8.0;
    double numSteps = numDegrees / 15.0;
    scaling *= pow(1.125, numSteps);
    updateGL();
}

void GLWidget::keyPressEvent( QKeyEvent *e )
{
  switch ( e->key() )
  {
  case Qt::Key_L:
    light = !light;
    if ( !light )
    {
      glDisable( GL_LIGHTING );
    }
    else
    {
      glEnable( GL_LIGHTING );
    }
    updateGL();
    break;
    //按下了L键，就可以切换是否打开光源。
    /*
  case Qt::Key_F:
    filter += 1;;
    if ( filter > 2 )
    {
      filter = 0;
    }
    updateGL();
    break;
    */
    //按下了F键，就可以转换一下所使用的纹理（就是变换了纹理滤波方式的纹理）。
  case Qt::Key_G:
    fogFilter += 1;;
    if ( fogFilter > 2 )
    {
      fogFilter = 0;
    }
    glFogi( GL_FOG_MODE, fogMode[fogFilter] );
    updateGL();
    break;
    //按下了G键，就可以变换一下所使用的雾的类型。
  case Qt::Key_PageUp:
    zoom -= 0.2;
    updateGL();
    break;
    //按下了PageUp键，将木箱移向屏幕内部。
  case Qt::Key_PageDown:
    zoom += 0.2;
    updateGL();
    break;
    //按下了PageDown键，将木箱移向屏幕外部。
  case Qt::Key_F2:
    fullscreen = !fullscreen;
    if ( fullscreen )
    {
      showFullScreen();
    }
    else
    {
      showNormal();
      setGeometry( 0, 0, 640, 480 );
    }
    update();
    break;
  case Qt::Key_Escape:
    close();
  }
}
